package org.charles.zhifou.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Min;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.charles.zhifou.common.annotation.AuthCheck;
import org.charles.zhifou.common.exception.ThrowUtils;
import org.charles.zhifou.common.response.BaseResponse;
import org.charles.zhifou.common.response.ResponseCodeEnum;
import org.charles.zhifou.common.response.ResponseUtils;
import org.charles.zhifou.common.utils.SqlUtils;
import org.charles.zhifou.enums.SqlSortOrderEnum;
import org.charles.zhifou.enums.UserRoleEnum;
import org.charles.zhifou.manager.ZhiPuAiManager;
import org.charles.zhifou.model.dto.question.*;
import org.charles.zhifou.model.entity.App;
import org.charles.zhifou.model.entity.Question;
import org.charles.zhifou.model.entity.User;
import org.charles.zhifou.model.vo.QuestionVO;
import org.charles.zhifou.model.vo.UserVO;
import org.charles.zhifou.service.AppService;
import org.charles.zhifou.service.QuestionService;
import org.charles.zhifou.service.UserService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.List;

/**
 * <h1>题目接口</h1>
 *
 * @author charles
 * @version 1.0
 */
@RestController
@RequestMapping("/question")
@Slf4j
@Tag(name = "题目接口")
public class QuestionController {

    @Resource
    private QuestionService questionService;

    @Resource
    private UserService userService;

    @Resource
    private AppService appService;

    @Resource
    private ZhiPuAiManager aiManager;

    // region 增删改查

    /**
     * 创建题目
     *
     * @param questionAddRequest 创建请求
     * @return 新创建题目 id
     */
    @PostMapping("/add")
    @Operation(summary = "创建题目")
    public BaseResponse<Long> addQuestion(@Validated @RequestBody QuestionAddRequest questionAddRequest) {
        // List 参数校验 (暂时在这里实现, 后面考虑如何使用 SpringBoot Validation 实现
        List<QuestionContentDTO> questionContent = questionAddRequest.getQuestionContent();
        ThrowUtils.throwIf(questionContent.isEmpty(), ResponseCodeEnum.PARAMS_ERROR, "题目个数为 0");
        questionContent.forEach(questionContentDTO -> {
            ThrowUtils.throwIf(StringUtils.isBlank(questionContentDTO.getTitle()), ResponseCodeEnum.PARAMS_ERROR, "题目标题不能为空");
            ThrowUtils.throwIf(questionContentDTO.getTitle().length() > 256, ResponseCodeEnum.PARAMS_ERROR, "题目标题长度不能超过 256 个字符");
            ThrowUtils.throwIf(questionContentDTO.getOptions().isEmpty(), ResponseCodeEnum.PARAMS_ERROR, "题目选项不能为空");
        });

        // 封装参数 (题目整体转为一个 JSON 字符串)
        Question question = new Question();
        question.setId(null);
        question.setQuestionContent(JSONUtil.toJsonStr(questionAddRequest.getQuestionContent()));
        question.setAppId(questionAddRequest.getAppId());
        question.setUserId(userService.getLoginUser().getId());

        // 返回新写入的数据 id
        Long newQuestionId = questionService.saveQuestion(question);
        return ResponseUtils.success(newQuestionId);
    }

    /**
     * 删除题目
     *
     * @param id 删除题目 id
     * @return 是否删除成功
     */
    @DeleteMapping("/delete")
    @Operation(summary = "删除题目")
    public BaseResponse<Boolean> deleteQuestion(
            @Min(value = 1, message = "id 必须大于 0")
            Long id
    ) {
        Boolean isSuccessful = questionService.removeQuestionById(id);
        return ResponseUtils.success(isSuccessful);
    }

    /**
     * 更新题目 (仅管理员)
     *
     * @param questionUpdateRequest 更新题目请求
     * @return 是否更新成功
     */
    @PostMapping("/update")
    @AuthCheck(neededRole = UserRoleEnum.ADMIN)
    @Operation(summary = "更新题目 (仅管理员)")
    public BaseResponse<Boolean> updateQuestion(@Validated @RequestBody QuestionUpdateRequest questionUpdateRequest) {
        // List 参数校验 (暂时在这里实现, 后面考虑如何使用 SpringBoot Validation 实现
        List<QuestionContentDTO> questionContent = questionUpdateRequest.getQuestionContent();
        ThrowUtils.throwIf(questionContent.isEmpty(), ResponseCodeEnum.PARAMS_ERROR, "题目个数为 0");
        questionContent.forEach(questionContentDTO -> {
            ThrowUtils.throwIf(StringUtils.isBlank(questionContentDTO.getTitle()), ResponseCodeEnum.PARAMS_ERROR, "题目标题不能为空");
            ThrowUtils.throwIf(questionContentDTO.getTitle().length() > 256, ResponseCodeEnum.PARAMS_ERROR, "题目标题长度不能超过 256 个字符");
            ThrowUtils.throwIf(questionContentDTO.getOptions().isEmpty(), ResponseCodeEnum.PARAMS_ERROR, "题目选项不能为空");
        });

        // 在此处将实体类和 DTO 进行转换
        Question question = new Question();
        question.setId(questionUpdateRequest.getId());
        question.setQuestionContent(JSONUtil.toJsonStr(questionUpdateRequest.getQuestionContent()));
        question.setAppId(null);
        question.setUserId(null);

        Boolean isSuccessful = questionService.updateQuestion(question);
        return ResponseUtils.success(isSuccessful);
    }

    /**
     * 根据 id 获取题目 VO
     *
     * @param id 题目 id
     * @return 题目信息
     */
    @GetMapping("/get_vo")
    @Operation(summary = "根据 id 获取题目 VO")
    public BaseResponse<QuestionVO> getQuestionVOById(
            @Min(value = 1, message = "id 必须大于 0")
            Long id
    ) {
        Question question = questionService.getById(id);
        ThrowUtils.throwIf(question == null, ResponseCodeEnum.NOT_FOUND_ERROR, "查询的题目不存在");

        // 额外添加创建用户信息
        QuestionVO questionVO = QuestionVO.entityToVo(question);
        User user = userService.getById(question.getUserId());
        UserVO userVO = UserVO.entityToVo(user);
        questionVO.setUser(userVO);

        return ResponseUtils.success(questionVO);
    }

    /**
     * 分页获取题目列表 (仅管理员)
     *
     * @param questionQueryRequest 查询请求
     * @return 题目列表
     */
    @PostMapping("/list/page")
    @AuthCheck(neededRole = UserRoleEnum.ADMIN)
    @Operation(summary = "分页获取题目列表 (仅管理员)")
    public BaseResponse<Page<Question>> listQuestionByPage(@Validated @RequestBody QuestionQueryRequest questionQueryRequest) {
        // 获取分页参数
        long current = questionQueryRequest.getCurrent();
        long size = questionQueryRequest.getPageSize();

        // 查询数据库
        Page<Question> questionPage = questionService.page(new Page<>(current, size), buildQueryWrapper(questionQueryRequest));
        return ResponseUtils.success(questionPage);
    }

    /**
     * 分页获取题目 VO 列表
     *
     * @param questionQueryRequest 查询请求
     * @return 题目 VO 列表
     */
    @PostMapping("/list/page_vo")
    @Operation(summary = "分页获取题目 VO 列表")
    public BaseResponse<Page<QuestionVO>> listQuestionVOByPage(@Validated @RequestBody QuestionQueryRequest questionQueryRequest) {
        // 获取分页参数
        long current = questionQueryRequest.getCurrent();
        long size = questionQueryRequest.getPageSize();

        // 查询数据库并封装返回
        Page<Question> questionPage = questionService.page(new Page<>(current, size), buildQueryWrapper(questionQueryRequest));
        Page<QuestionVO> questionVOPage = new Page<>(current, size, questionPage.getTotal());
        questionVOPage.setRecords(questionPage.getRecords().stream().map(QuestionVO::entityToVo).toList());

        return ResponseUtils.success(questionVOPage);
    }

    /**
     * 分页获取当前登录用户创建的题目列表
     *
     * @param questionQueryRequest 查询请求
     * @return 题目 VO 列表
     */
    @PostMapping("/my/list/page_vo")
    @Operation(summary = "分页获取当前登录用户创建的题目列表")
    public BaseResponse<Page<QuestionVO>> listMyQuestionVOByPage(@Validated @RequestBody QuestionQueryRequest questionQueryRequest) {
        // 获取分页参数
        long current = questionQueryRequest.getCurrent();
        long size = questionQueryRequest.getPageSize();

        // 额外添加 userId 限定
        questionQueryRequest.setUserId(userService.getLoginUser().getId());

        // 查询数据库并封装返回
        Page<Question> questionPage = questionService.page(new Page<>(current, size), buildQueryWrapper(questionQueryRequest));
        Page<QuestionVO> questionVOPage = new Page<>(current, size, questionPage.getTotal());
        questionVOPage.setRecords(questionPage.getRecords().stream().map(QuestionVO::entityToVo).toList());
        return ResponseUtils.success(questionVOPage);
    }

    /**
     * 编辑题目
     *
     * @param questionEditRequest 编辑题目请求
     * @return 是否编辑成功
     */
    @PostMapping("/edit")
    @Operation(summary = "编辑题目")
    public BaseResponse<Boolean> editQuestion(@Validated @RequestBody QuestionEditRequest questionEditRequest) {
        // List 参数校验 (暂时在这里实现, 后面考虑如何使用 SpringBoot Validation 实现
        List<QuestionContentDTO> questionContent = questionEditRequest.getQuestionContent();
        ThrowUtils.throwIf(questionContent.isEmpty(), ResponseCodeEnum.PARAMS_ERROR, "题目个数为 0");
        questionContent.forEach(questionContentDTO -> {
            ThrowUtils.throwIf(StringUtils.isBlank(questionContentDTO.getTitle()), ResponseCodeEnum.PARAMS_ERROR, "题目标题不能为空");
            ThrowUtils.throwIf(questionContentDTO.getTitle().length() > 256, ResponseCodeEnum.PARAMS_ERROR, "题目标题长度不能超过 256 个字符");
            ThrowUtils.throwIf(questionContentDTO.getOptions().isEmpty(), ResponseCodeEnum.PARAMS_ERROR, "题目选项不能为空");
        });

        // 封装参数
        Question question = new Question();
        question.setId(questionEditRequest.getId());
        question.setQuestionContent(JSONUtil.toJsonStr(questionEditRequest.getQuestionContent()));
        question.setAppId(null);
        question.setUserId(null);

        // 更新并返回结果
        Boolean isSuccessful = questionService.editQuestion(question);
        return ResponseUtils.success(isSuccessful);
    }

    // endregion

    // region AI 相关

    /**
     * 生成题目的用户消息
     *
     * @param app
     * @param questionNumber
     * @param optionNumber
     * @return
     */
    private String getGenerateQuestionUserMessage(App app, int questionNumber, int optionNumber) {
        return app.getAppName() + "，\n" +
                "$$$" + app.getAppDesc() + "$$$" + "，\n" +
                app.getAppType().getValue() + "，\n" +
                questionNumber + "，\n" +
                optionNumber;
    }

    /**
     * AI 生成题目 (同步)
     * @param aiGenerateQuestionRequest AI 生成题目请求
     * @return 题目列表
     */
    @PostMapping("/ai_generate")
    @Operation(summary = "AI 生成题目 (同步)")
    public BaseResponse<List<QuestionContentDTO>> aiGenerateQuestion(@Validated @RequestBody AiGenerateQuestionRequest aiGenerateQuestionRequest) {
        // 获取参数
        Long appId = aiGenerateQuestionRequest.getAppId();
        int questionNumber = aiGenerateQuestionRequest.getQuestionNumber();
        int optionNumber = aiGenerateQuestionRequest.getOptionNumber();

        // 获取应用信息, 封装 Prompt
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ResponseCodeEnum.NOT_FOUND_ERROR, "应用不存在");
        String userMessage = getGenerateQuestionUserMessage(app, questionNumber, optionNumber);

        // 生成题目列表
        List<QuestionContentDTO> questionContentDTOList = questionService.aiGenerateQuestionsSync(appId, userMessage);
        return ResponseUtils.success(questionContentDTOList);
    }

    /**
     * AI 生成题目 (异步)
     * @param aiGenerateQuestionRequest AI 生成题目请求
     * @return 题目列表
     */
    // 注意: 使用 SSE 必须用 Get 请求
    @Operation(summary = "AI 生成题目 (异步)")
    @GetMapping("/ai_generate/sse")
    public SseEmitter aiGenerateQuestionSSE(@Validated AiGenerateQuestionRequest aiGenerateQuestionRequest) {
        // 获取参数
        Long appId = aiGenerateQuestionRequest.getAppId();
        int questionNumber = aiGenerateQuestionRequest.getQuestionNumber();
        int optionNumber = aiGenerateQuestionRequest.getOptionNumber();

        // 获取应用信息, 封装 Prompt
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ResponseCodeEnum.NOT_FOUND_ERROR);
        String userMessage = getGenerateQuestionUserMessage(app, questionNumber, optionNumber);

        return questionService.aiGenerateQuestionsAsync(appId, userMessage);
    }

    // endregion

    public QueryWrapper<Question> buildQueryWrapper(QuestionQueryRequest questionQueryRequest) {
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        if (questionQueryRequest == null) {
            return queryWrapper;
        }

        Long id = questionQueryRequest.getId();
        String questionContent = questionQueryRequest.getQuestionContent();
        Long appId = questionQueryRequest.getAppId();
        Long userId = questionQueryRequest.getUserId();
        Long notId = questionQueryRequest.getNotId();
        String sortField = StrUtil.toUnderlineCase(questionQueryRequest.getSortField());
        SqlSortOrderEnum sortOrder = questionQueryRequest.getSortOrder();

        // 模糊查询
        queryWrapper.like(StringUtils.isNotBlank(questionContent), "question_content", questionContent);
        // 精确查询
        queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(appId), "app_id", appId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "user_id", userId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), !SqlSortOrderEnum.DESC.equals(sortOrder), sortField);

        return queryWrapper;
    }
}

















